<?php

namespace App\Support\CustomForm\Attribute;

use App\Http\Form\AnalysisFormContent;
use App\Support\CustomForm\Validate\ValidateSupport;
use Closure;
use Illuminate\Support\Collection;

class AttributeSupport
{
    public static ?AttributeMapSupport $attributesMap = null;

    protected bool $isResources = false;

    public $defaultVal;

    /**
     * 输入框属性name值
     *
     * @var string
     */
    protected string $inputName;

    /**
     * 输入框描述
     *
     * @var string
     */
    protected string $inputDesc;

    /**
     * 输入框类型
     *
     * @var string
     */
    protected string $inputType;

    /**
     * @var string|array|bool|Closure
     */
    protected $value = null;

    /**
     * 校验规则
     *
     * @var ValidateSupport|null
     */
    protected ?ValidateSupport $validateSupport = null;

    /**
     * @var Closure|null|string
     */
    protected $handleShowFunc = null;

    /**
     * @var null|Closure|string
     */
    protected $handleSaveFunc = null;

    /**
     * @var array
     */
    protected array $relations = [];

    /**
     * @var array
     */
    protected array $allRelations = [];

    /**
     * 输入框自定义属性值
     *
     * @var array
     */
    protected array $customAttributes = [];

    public string $errMsg = "";

    public ?Closure $afterHandle = null;

    public ?Closure $beforeBuildHandle = null;


    const INPUT_TYPE_MOBILE     = "mobile";
    const INPUT_TYPE_STRING     = "string";
    const INPUT_TYPE_NUMBER     = "number";
    const INPUT_TYPE_TEXT       = "textarea";
    const INPUT_TYPE_IMG        = "img";
    const INPUT_TYPE_IMG_GROUP  = "imgGroup";
    const INPUT_VIDEO           = "video";
    const INPUT_VIDEO_URL       = "video-url";
    const INPUT_DATE_YMD        = "date-ymd";
    const INPUT_DATE            = "date";
    const INPUT_DATE_TIME_RANGE = "date-time-range";
    const INPUT_DATE_RANGE      = "date-range";
    const INPUT_DATETIME        = "datetime";
    const INPUT_TYPE_RADIO      = "radio";
    const INPUT_TYPE_SELECT     = "select";
    const INPUT_TYPE_ADDRESS    = "address";
    const INPUT_TYPE_FY_ADDRESS = "fy-address";
    const INPUT_NEW_DATE        = "newDate";//前端 滚轮时间选择


    /**
     * @param string $inputName
     * @param string $inputType
     * @param string $inputDesc
     * @param bool $isResources
     */
    protected function __construct(string $inputName, string $inputType, string $inputDesc, bool $isResources)
    {
        $this->inputName   = $inputName;
        $this->inputType   = $inputType;
        $this->inputDesc   = $inputDesc;
        $this->isResources = $isResources;
    }

    /**
     * @param string $inputName
     * @param string $inputType
     * @param string $inputDesc
     * @param bool $isResources
     * @return AttributeSupport
     */
    public static function getInstance(
        string $inputName,
        string $inputType,
        string $inputDesc,
        bool $isResources = false
    ): AttributeSupport {
        if (is_null(self::$attributesMap)) {
            self::$attributesMap = new AttributeMapSupport();
        }
        if (!property_exists(self::$attributesMap, $inputName)) {
            self::$attributesMap->{$inputName} = new static($inputName, $inputType, $inputDesc, $isResources);
        }
        return self::$attributesMap->{$inputName};
    }

    /**
     * @param bool $isResources
     * @return $this
     */
    public function setResources(bool $isResources = true): AttributeSupport
    {
        $this->isResources = $isResources;
        return $this;
    }

    /**
     * @param string $inputName
     * @return $this
     */
    public function setInputName(string $inputName): AttributeSupport
    {
        $this->inputName = $inputName;
        return $this;
    }

    /**
     * @return string
     */
    public function getInputName(): string
    {
        return $this->inputName;
    }

    /**
     * @return string
     */
    public function getInputType(): string
    {
        return $this->inputType;
    }

    /**
     * @return string
     */
    public function getInputDesc(): string
    {
        return $this->inputDesc;
    }

    /**
     * @param string $type
     * @return $this
     */
    public function setInputType(string $type = self::INPUT_TYPE_STRING): AttributeSupport
    {
        $this->inputType = $type;
        return $this;
    }

    /**
     * @param string $desc
     * @return $this
     */
    public function setInputDesc(string $desc): AttributeSupport
    {
        $this->inputDesc = $desc;
        return $this;
    }

    /**
     * @param ValidateSupport $validateSupport
     * @return $this
     */
    public function setValidateRules(ValidateSupport $validateSupport): AttributeSupport
    {
        $this->validateSupport = $validateSupport;
        return $this;
    }

    /**
     * @return ValidateSupport|null
     */
    public function getValidateSupport(): ?ValidateSupport
    {
        return $this->validateSupport;
    }


    /**
     * @param mixed ...$attributes
     * @return $this
     */
    public function setCustomAttribute(array ...$attributes): AttributeSupport
    {
        foreach ($attributes as $attribute) {
            $this->customAttributes = array_merge($this->customAttributes, [
                $attribute[0] => $attribute[1]
            ]);
        }
        return $this;
    }

    /**
     * 设置可驳回
     *
     * @param array $rejectOptions
     * @return $this|AttributeSupport
     */
    public function setReject(array $rejectOptions = []): AttributeSupport
    {
        return $this->setCustomAttribute(["reject", true])->setCustomAttribute([
            "rejectOptions",
            count($rejectOptions) ? $rejectOptions : ["内容需修改"]
        ]);
    }

    /**
     * @param string $placeholder
     * @return $this|AttributeSupport
     */
    public function setPlaceholder(string $placeholder): AttributeSupport
    {
        return $this->setCustomAttribute(["placeholder", $placeholder]);
    }


    /**
     * @param array $images
     * @return $this|AttributeSupport
     */
    public function setExampleImages(array $images): AttributeSupport
    {
        return $this->setCustomAttribute(["descImg", $images]);
    }

    /**
     * @param array $videos
     * @return $this|AttributeSupport
     */
    public function setExampleVideos(array $videos): AttributeSupport
    {
        return $this->setCustomAttribute(["descVideo", $videos]);
    }

    /**
     * @param array $files
     * @return $this|AttributeSupport
     */
    public function setExampleFiles(array $files): AttributeSupport
    {
        return $this->setCustomAttribute(["descFile", $files]);
    }


    /**
     * @param array $optionEnum
     * @return $this|AttributeSupport
     */
    public function setOptions(array $optionEnum): AttributeSupport
    {
        return $this->setCustomAttribute(
            [
                "options",
                ValidateSupport::buildOptionsFromEnum($optionEnum)
            ]
        );
    }

    /**
     * @param string $remark
     * @return $this|AttributeSupport
     */
    public function setRemark(string $remark): AttributeSupport
    {
        return $this->setCustomAttribute(["desc", $remark]);
    }

    const DATE_FORMAT_1 = "YYYY-MM-DD";
    const DATE_FORMAT_2 = "YYYY-MM";
    const DATE_FORMAT_3 = "MM_DD";
    const DATE_FORMAT_4 = "YYYY-MM-DD HH:ii:ss";
    const DATE_FORMAT_5 = "HH:ii";

    /**
     * @param string $format
     * @return $this|AttributeSupport
     */
    public function setDateFormat(string $format = self::DATE_FORMAT_1): AttributeSupport
    {
        return $this->setCustomAttribute(
            ["format", $format]
        );
    }

    /**
     * @param array ...$relations
     * @return $this
     */
    public function setRelations(array ...$relations): AttributeSupport
    {
        $this->relations = [];
        foreach ($relations as $relation) {
            array_push($this->relations, [
                "field" => $relation[0],
                "value" => $relation[1]
            ]);
        }
        return $this;
    }

    /**
     * @param array $relations
     * @return $this
     */
    public function setAllRelations(array $relations): AttributeSupport
    {
        $tempRelations = [];
        foreach ($relations as $relationKey => $relationVal) {
            array_push($tempRelations, [
                "field" => $relationKey,
                "value" => $relationVal
            ]);
        }
        array_push($this->allRelations, $tempRelations);
        return $this;
    }

    /**
     * @param Closure|string $closure
     * @return $this
     */
    public function registerHandleShowValueFunc($closure): AttributeSupport
    {
        $this->handleShowFunc = $closure;
        return $this;
    }

    /**
     * @param Closure|string $closure
     * @return $this
     */
    public function registerHandleSaveValueFunc($closure): AttributeSupport
    {
        $this->handleSaveFunc = $closure;
        return $this;
    }

    /**
     * @return Closure|string|null
     */
    public function getHandleSaveValueFunc()
    {
        return $this->handleSaveFunc;
    }

    /**
     * @return Closure|string|null
     */
    public function getHandleShowValueFunc()
    {
        return $this->handleShowFunc;
    }

    /**
     * @param Closure $closure
     * @return $this
     */
    public function setBeforeBuildHandle(Closure $closure): AttributeSupport
    {
        $this->beforeBuildHandle = $closure;
        return $this;
    }

    /**
     * @return Closure|null
     */
    public function getBeforeBuildHandle(): ?Closure
    {
        return $this->beforeBuildHandle;
    }

    /**
     * 渲染展示值
     *s
     * @param Collection $formContent
     * @param null $default
     * @return AttributeSupport
     */
    public function setValue(Collection $formContent, $default = null): AttributeSupport
    {

        if ($this->getIsResources() && empty($default)) {
            $default = [];
        }
        $this->defaultVal = $default;
        $val              = $formContent->get($this->inputName, $default);
        $this->value      = ($handleShowValueFunc = $this->getHandleShowValueFunc()) ? $handleShowValueFunc($val) : $val;
        return $this;
    }

    /**
     * @param string $errMsg
     * @return $this
     */
    public function setErrMsg(string $errMsg): AttributeSupport
    {
        $this->errMsg = $errMsg;
        return $this;
    }

    /**
     * @return array
     */
    public function build(): array
    {
        if ($this->getBeforeBuildHandle()) {
            ($this->getBeforeBuildHandle())($this);
        }
        return [
            "field"            => $this->inputName,
            "text"             => $this->inputDesc,
            "inputType"        => $this->inputType,
            "validate"         => $this->validateSupport ? $this->validateSupport->build() : new \stdClass(),
            "customAttributes" => empty($this->customAttributes) ? new \stdClass() : $this->customAttributes,
            "relations"        => $this->relations,
            "allRelations"     => $this->allRelations,
            "value"            => $this->value,
            "errMsg"           => $this->errMsg,
        ];
    }

    /**
     * @param array $params
     * @param null $default
     * @return mixed|null
     */
    public function handleShowValue(array $params = [], $default = null)
    {
        $value = $default;
        if (isset($params[$this->inputName])) {
            if ($this->handleShowFunc) {
                $value = ($this->handleShowFunc)($params[$this->inputName]);
            } else {
                $value = $params[$this->inputName];
            }
        }
        return $value;
    }

    /**
     * @return bool
     */
    public function getIsResources(): bool
    {
        return $this->isResources;
    }


    /**
     * @param Closure $closure
     * @return $this
     */
    public function setAfterHandle(Closure $closure): AttributeSupport
    {
        $this->afterHandle = $closure;
        return $this;
    }

    /**
     * @return Closure|null
     */
    public function getAfterHandle(): ?Closure
    {
        return $this->afterHandle;
    }


    /**
     * @param $name
     * @return AttributeSupport
     */
    public function __get($name)
    {
        // TODO: Implement __get() method.
        return self::$attributesMap->{$name};
    }
}

